package com.npave.zryhadapter.config;

import com.npave.zryhadapter.exceptions.BusinessException;
import com.npave.zryhadapter.response.ReturnMsg;
import com.npave.zryhadapter.response.vo.JsonObj;
import com.npave.zryhadapter.utils.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author zejiang wu
 * @date 2022/6/30
 * @description
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理接口参数缺失异常
     * @param request
     * @param ex
     * @param response
     * @return
     */
    @ExceptionHandler({MissingServletRequestParameterException.class, HttpMessageNotReadableException.class})
    @ResponseBody
    public ResponseEntity<JsonObj> handleMissingRequestParameterException(HttpServletRequest request,
                                                                          Exception ex,
                                                                          HttpServletResponse response){
        response.reset();
        return new ResponseEntity<>(ResponseUtil.error(ReturnMsg.PARAM_ERROR), HttpStatus.OK);
    }

    /**
     * 功能:处理普通参数校验失败的异常
     * @param request
     * @param ex
     * @param response
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ResponseEntity<JsonObj> handleConstraintViolationException(HttpServletRequest request,
                                                                      ConstraintViolationException ex,
                                                                      HttpServletResponse response){
        response.reset();
        JsonObj jsonObj = new JsonObj();
        jsonObj.setError(ReturnMsg.PARAM_ERROR);

        Set<String> errorInfoSet = new TreeSet<>();
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        if (!violations.isEmpty()) {
            // 设置验证结果状态码
            for (ConstraintViolation<?> item : violations) {
                // 遍历错误字段信息
                errorInfoSet.add(item.getMessage());
                log.error("[{}]{}", item.getPropertyPath(), item.getMessage());
            }
            StringBuilder sbf = new StringBuilder();
            for (String errorInfo : errorInfoSet) {
                sbf.append(errorInfo);
                sbf.append(",");
            }
            jsonObj.setMsg(sbf.substring(0, sbf.length() - 1));
        }

        return new ResponseEntity<>(jsonObj, HttpStatus.OK);
    }

    /**
     * 功能: 处理实体类参数校验失败的异常
     * @param request
     * @param ex
     * @param response
     * @return
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public ResponseEntity<JsonObj> handleBindException(HttpServletRequest request,
                                                       BindException ex,
                                                       HttpServletResponse response) {
        response.reset();
        JsonObj jsonObj = new JsonObj();
        jsonObj.setError(ReturnMsg.PARAM_ERROR);

        if(ex.hasErrors()) {
            // 设置验证结果状态码
            // 获取错误字段信息集合
            List<FieldError> fieldErrorList = ex.getFieldErrors();

            // 使用TreeSet是为了让输出的内容有序输出(默认验证的顺序是随机的)
            Set<String> errorInfoSet = new TreeSet<String>();
            for (FieldError fieldError : fieldErrorList) {
                // 遍历错误字段信息
                errorInfoSet.add(fieldError.getDefaultMessage());
                log.error("[{}.{}]{}", fieldError.getObjectName() , fieldError.getField(), fieldError.getDefaultMessage());
            }

            StringBuilder sbf = new StringBuilder();
            for (String errorInfo : errorInfoSet) {
                sbf.append(errorInfo);
                sbf.append(",");
            }
            jsonObj.setMsg(sbf.substring(0, sbf.length() - 1));
        }

        return new ResponseEntity<>(jsonObj, HttpStatus.OK);
    }

    /**
     * 请求方式错误捕获
     * @param request
     * @param ex
     * @param response
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public ResponseEntity<JsonObj> handleRequestMethodNotSupportedException(HttpServletRequest request,
                                                                            Exception ex,
                                                                            HttpServletResponse response){
        response.reset();
        return new ResponseEntity<>(ResponseUtil.error(ReturnMsg.SERVICE_NOT_FORBID), HttpStatus.OK);
    }

    /**
     * 未捕获异常处理
     * @param request
     * @param exception
     * @param response
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseEntity<JsonObj> handleNotFoundException(HttpServletRequest request,
                                                           Exception exception,
                                                           HttpServletResponse response) {
        response.reset();
        //记录异常信息
        StackTraceElement stackTraceElement = exception.getStackTrace()[0];
        log.error("------------------异常信息：{}", exception.getLocalizedMessage());
        log.error("------------------异常信息所在类名：{}", stackTraceElement.getClassName());
        log.error("------------------异常信息所在方法名：{}", stackTraceElement.getMethodName());
        log.error("------------------异常信息所在行：{}", stackTraceElement.getLineNumber());
        log.error("------------------异常信息：{}", exception.getMessage());
        log.error("详细异常",exception);
        if (exception instanceof NoHandlerFoundException) { //404异常处理
            return new ResponseEntity<>(ResponseUtil.error(ReturnMsg.SERVICE_NOT_FORBID), HttpStatus.NOT_FOUND);
        }else {  //500异常
            return new ResponseEntity<>(ResponseUtil.error(ReturnMsg.SYSTEM_EXCEPTION), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 业务异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ResponseEntity<JsonObj> handleBusinessException(BusinessException e) {
        log.error("业务异常:"  + e.result);
        return new ResponseEntity<>(ResponseUtil.error(e.result), e.status);
    }

}
